-
Notifications
You must be signed in to change notification settings - Fork 99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add share/unshare for class objects #180
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have mixed feelings about the unsafe
route to create the object.
What's the use case you had in mind? (Why not wait until we can check the invariants?)
(A.freeze table, _count) | ||
}; | ||
|
||
/// Put purely-functional representation into class. Need to make sure the tree is constructed with the same Eq and Hash functions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I vote that we either write those checking functions and use them, or remove this until we have them?
Is this a performance thing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main use case is for upgrade. We cannot make HashMap stable, so the upgrade code would look like:
stable var pureHashMap : HashMap.Tree = [];
flexible let db: HashMap.HashMap = HashMap.unshareUnsafe(pureHashMap);
system func preupgrade() {
pureHashMap := HashMap.share();
};
The db can be very large when upgrade, it's probably too expensive to check for the invariants.Also for the upgrade case, the invariants always hold.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious how BigMap handles upgrade?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't handle it at the moment, but similar issues will arise (how to enforce invariants for the serialized representation without rechecking them).
Also, I think this question will arise for each developer, each time they implement such functions, for any canister with data invariants (so, almost all of them).
Perhaps we should add some notes in the design doc for share
and unshare
that just clarifies this design point (Do we expect these functions to be defensive and re-establish invariants, or to be trusting and assume that they hold, as with your unsafe
variant?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://github.com/dfinity/motoko-base/blob/master/doc/design.md#classes
Currently reads as follows:
The
share
/unshare
functions of a class need to convert to a type that is designed for stability (potentially, including extensibility) and space efficiency, not for enabling efficient direct operations.
If we follow this guide, it seems like we should be using a vector of key-value pairs for things like BigMap, and for other map-like structures, and shouldn't even include the hash values if they were computed for internal use only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, if at all possible, let's make such functions safe. Otherwise you break encapsulation. I doubt that the copying with freeze and thaw are significantly cheaper than a safe alternative.
Also, for polymorphic data structures, these functions need to be parameterised over the share/unshare functions for each type parameter, otherwise you cannot compose them properly.
if (count == 0) { | ||
elems := [var]; | ||
}; | ||
elems := Prim.Array_init<X>(count, xs[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an else
missing here? Otherwise this will trap when empty.
(A.freeze table, _count) | ||
}; | ||
|
||
/// Put purely-functional representation into class. Need to make sure the tree is constructed with the same Eq and Hash functions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, if at all possible, let's make such functions safe. Otherwise you break encapsulation. I doubt that the copying with freeze and thaw are significantly cheaper than a safe alternative.
Also, for polymorphic data structures, these functions need to be parameterised over the share/unshare functions for each type parameter, otherwise you cannot compose them properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cleaning up my review queue
d52aecd
to
08507fc
Compare
share
/unshare
function forRBTree
,HashMap
andBuffer
. This is useful for supporting upgrade, which only works for shareable types.